// System includes
#include <QDebug>
#include <QPainter>
#include <QStyle>
#include <QStyleOption>
#include <cmath>

// Local includes
#include "yigcablegraphic.h"
#include "graphics/yigcolorkit.h"
#include "graphics/yigsynthgraphic.h"

#define  PI 3.141592654

YigCableGraphic::YigCableGraphic(const QLineF &line, QString inputKey, QString outputKey, CableOutputType outputType,
                                 int audioInputIndex, QGraphicsLineItem *parent) :
    QGraphicsLineItem(line, parent),
    mInputKey(inputKey),
    mOutputKey(outputKey),
    mAudioInputIndex(audioInputIndex)
{
    //mOutputType = outputType;
    //pen.setWidth(2);
    //pen.setCapStyle(Qt::RoundCap);
    //pen.setJoinStyle(Qt::RoundJoin);
    //pen.setStyle(Qt::DotLine);
    //pen.setColor(QColor(0, 0, 0,255));
    /*
    QColor cableColor = YigColorKit::accent;
    cableColor.setAlpha(200);

    if(outputType == AUDIO)
    {
        setZValue(-5);
    }

    else
    {
        setZValue(-20);
    }

    pen.setColor(cableColor);
    //pen.setStyle(Qt::DashLine);
    pen.setWidth(3);
    pen.setColor(QColor(0,0,0,0));
    setPen(pen);
    //setZValue(-10);*/


    // Create output triangle shape and parent to the cable

    /*
    QVector<QPointF> pointVector;
    pointVector.append(QPoint(0, 0));
    pointVector.append(QPoint(5, 19));
    pointVector.append(QPoint(0,13));
    pointVector.append(QPoint(-6, 19));
    QPolygonF polygon(pointVector);
    if(outputType == AUDIO)
    {
        polygon.translate(0, YigSynthGraphic::AUDIO_ELLIPSE_SIZE/2);
    }

    else if(outputType == MODULATION)
    {
        polygon.translate(0, YigSynthGraphic::MOD_ELLIPSE_SIZE/2);
    }

    outputTriangle = new QGraphicsPolygonItem();
    //scene()->addItem(outputTriangle);
    outputTriangle->setPen(pen);
    outputTriangle->setPolygon(polygon);
    outputTriangle->setFlag( QGraphicsItem::ItemIsSelectable, false );
    outputTriangle->setFlag( QGraphicsItem::ItemIsMovable, false );
    outputTriangle->setFlag( QGraphicsItem::ItemStacksBehindParent, false);
    //outputTriangle->setCacheMode(QGraphicsItem::ItemCoordinateCache);
    outputTriangle->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
    outputTriangle->setPen(pen);
    outputTriangle->setZValue(20);
    */
    //outputTriangle->setZValue(10);
    //outputTriangle->moveBy(30, 30);

    /*
    setFlag( QGraphicsItem::ItemIsSelectable, false );
    halfArrow = new QGraphicsLineItem(this);
    halfArrow->setFlag( QGraphicsItem::ItemIsSelectable, false );
    halfArrow->setFlag( QGraphicsItem::ItemIsMovable, true );
    //halfArrow->setCacheMode( QGraphicsItem::ItemCoordinateCache );

    halfArrow->setPen(pen);
    */
    setFlag(QGraphicsItem::ItemHasNoContents);
    //outputTriangle->setFlag(QGraphicsItem::ItemHasNoContents);
    setOutputType(outputType);
    autoCounter = 0;
}

YigCableGraphic::~YigCableGraphic()
{
    /*
    while(!lineList.isEmpty())
    {
        delete lineList.takeFirst();
    }
    lineList.clear();
    */
    //scene()->removeItem(outputTriangle);
    //delete outputTriangle;
}

void YigCableGraphic::addArrow()
{
    if(scene())
    {
        //scene()->addItem(outputTriangle);
    }
}

void YigCableGraphic::removeArrow()
{
    if(scene())
    {
        //scene()->removeItem(outputTriangle);
    }
}

QString YigCableGraphic::getInputKey()
{
    return mInputKey;
}


QString YigCableGraphic::getOutputKey()
{
    return mOutputKey;
}


void YigCableGraphic::setOutputKey(QString outputKey)
{
    mOutputKey = outputKey;
}


YigCableGraphic::CableOutputType YigCableGraphic::getOutputType()
{
    return mOutputType;
}

int YigCableGraphic::getAudioInputIndex()
{
    return mAudioInputIndex;
}

void YigCableGraphic::setOutputType(YigCableGraphic::CableOutputType outputType)
{
    mOutputType = outputType;
    /*
    if(outputType == AUDIO)
    {
        //pen.setStyle(Qt::SolidLine);
        //QColor cableColor = YigColorKit::focus1Highlight;
        QColor cableColor = YigColorKit::background2;
        cableColor.setAlpha(220);
        pen.setColor(cableColor);
        pen.setWidth(3);
        //pen.setWidth(3);
        QVector<qreal> dashes;
        qreal space = 6;
        dashes << 2 << space;
        pen.setDashPattern(dashes);
        setPen(pen);
        //cableColor = YigColorKit::background2;
        //arrowColor = YigColorKit::focus1Highlight;
        //arrowColor = YigColorKit::background2;
        arrowColor = cableColor;
        //pen.setColor(arrowColor);
        outputTriangle->setPen(Qt::NoPen);
        arrowColor.setAlpha(200);
        outputTriangle->setBrush(QBrush(arrowColor));
        setZValue(-5);
    }

    else
    {
        pen.setStyle(Qt::SolidLine);
        QColor cableColor = YigColorKit::accent;
        cableColor.setAlpha(240);
        //QColor cableColor = YigColorKit::focus1Highlight;
        //QColor cableColor = YigColorKit::background2;
        //cableColor.setAlpha(180);
        pen.setColor(cableColor);
        pen.setWidth(1);
        //pen.setCosmetic(true);
        //pen.setDashOffset(1);

        //QVector<qreal> dashes;
        //qreal space = 4;
        //dashes << 2 << space;
        //pen.setDashPattern(dashes);
        setPen(pen);

        arrowColor = YigColorKit::accent;
        //pen.setStyle(Qt::SolidLine);
        cableColor = YigColorKit::background2;
        cableColor.setAlpha(240);
        setZValue(-20);
        //pen.setColor(arrowColor);
        outputTriangle->setPen(Qt::NoPen);
        arrowColor.setAlpha(200);
        outputTriangle->setBrush(QBrush(arrowColor));
    }*/

    //halfArrow->setPen(pen);
    //redrawHalfArrow();
}

void YigCableGraphic::setPoint1(QPoint point)
{
    //QLineF newLine(line());
    //newLine.setP1(point);
    //this->setLine(newLine);
    setLine(point.x(), point.y(), line().p2().x(), line().p2().y());
    //redrawHalfArrow();
    autoDisconnect();
}

void YigCableGraphic::setPoint2(QPoint point)
{
    //QLineF newLine(line());
    //newLine.setP2(point);
    //this->setLine(newLine);
    setLine(line().p1().x(), line().p1().y(), point.x(), point.y());
    //redrawHalfArrow();
    autoDisconnect();
}

void YigCableGraphic::updateCurrentPoint(QPoint point)
{
    QLineF newLine(line());
    newLine.setP2(point);
    setLine(newLine);
    //QLineF segment(lineList.last()->line());
    //lineList.last()->setLine(segment.p1().x(), segment.p1().y(), point.x(), point.y());
}

void YigCableGraphic::updatePos( const QPointF& point )
{

}

void YigCableGraphic::itemClick(QGraphicsItem* item)
{

}

/*
QGraphicsLineItem* YigCableGraphic::addLineSegment(QPoint point)
{

    QLineF segment;

    if(lineList.isEmpty())
    {
        segment.setP1(point);
        segment.setP2(point);
    }

    else
    {
        segment.setP1(lineList.last()->line().p2());
        segment.setP2(point);
    }

    YigGraphicsLineItem* newLine = new YigGraphicsLineItem(this, segment);
    newLine->setPen(pen);
    lineList.append(newLine);
    return newLine;

}
*/

void YigCableGraphic::removeFromScene(QGraphicsScene *scene)
{
    /*
    for(int i = 0; i < lineList.size(); i++)
    {
        scene->removeItem(lineList.at(i));
    }
    */
}

void YigCableGraphic::mouseClickEndPoint(QPoint point)
{
    //line().setP2(point);
    //lineList.removeLast();
    //QLineF segment(lineList.last()->line());
    //lineList.last()->setLine(segment.p1().x(), segment.p1().y(), point.x(), point.y());
}

void YigCableGraphic::setSelectable(bool sectableBool)
{
    //setFlag(QGraphicsItem::ItemIsSelectable, sectableBool);
    /*
    for(int i = 0; i < lineList.size(); i++)
    {
        lineList[i]->setFlag( QGraphicsItem::ItemIsSelectable, sectableBool );
    }
    */
}

void YigCableGraphic::setMapKey(QString mapKey)
{
    mMapKey = mapKey;
}

QString YigCableGraphic::getMapKey()
{
    return mMapKey;
}

/*
void YigCableGraphic::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QStyleOptionGraphicsItem myOption = (*option);
    myOption.state &= !QStyle::State_Selected;
    QGraphicsLineItem::paint(painter, &myOption, widget);

    // Paint our highlight here
    if(option->state & QStyle::State_Selected)
    {
        painter->setPen(YigBaseGraphic::highlightPen);
        painter->drawLine(line());
    }
}
*/

void YigCableGraphic::keyboardDelete()
{
    emit cableKeyboardDeleted(mMapKey);
}

void YigCableGraphic::autoDisconnect()
{
    if(autoCounter >= 10)
    {
        if(line().length() >= 350)
        {
            keyboardDelete();
        }

        autoCounter = 0;
    }

    else
    {
        autoCounter++;
    }
}

void YigCableGraphic::redrawHalfArrow()
{   
    //halfArrow->setPos(0, 0);
    //halfArrow->setLine(0, 0, ARROW_SIZE, -5);
    //qreal angle = atan2(line().p2().y() - line().p1().y(), line().p2().x() - line().p1().x()) * 180 / PI;
    //outputTriangle->setPos(0,0);
    //halfArrow->setRotation(angle - 165);
    //halfArrow->moveBy(line().p2().x(), line().p2().y());

    /*
    if(mOutputType == MODULATION)
    {

        qreal angleOffset = angle+90;
        qreal angleB = atan2(YigSynthGraphic::AUDIO_INPUT_X - YigSynthGraphic::MOD_INPUT_X,
                             YigSynthGraphic::AUDIO_INPUT_Y - YigSynthGraphic::MOD_INPUT_Y) * 180 / PI;
        qreal angleC = sqrt( (angleOffset * angleOffset) + (angleB * angleB) );
        //qDebug() << "ANGLE C: " << angleC;
        //outputTriangle->setRotation(angle);
        outputTriangle->setPos(0,0);

        outputTriangle->moveBy(line().p2().x() - YigSynthGraphic::MOD_INPUT_X + (YigSynthGraphic::ELLIPSE_SIZE/2),
                               line().p2().y() - YigSynthGraphic::MOD_INPUT_Y + (YigSynthGraphic::ELLIPSE_SIZE/2));

        qreal angle2 = atan2(outputTriangle->pos().x() - line().p2().x(), outputTriangle->pos().y() - line().p2().y()) * 180 / PI;
        qreal angle3 = atan2(outputTriangle->pos().x() - line().p1().x(), outputTriangle->pos().y() - line().p1().y()) * 180 / PI;
        qreal angle4 = atan2(line().p1().x() - outputTriangle->pos().x(),
                             (line().p1().y() - outputTriangle->pos().y() - (YigSynthGraphic::ELLIPSE_SIZE/2))) * 180 / PI;
        //outputTriangle->setRotation(angle - angle2 + 90 + 45);
        outputTriangle->setRotation(-angle3 - angleB);
        //qDebug() << angle3;



        //outputTriangle->moveBy(line().p2().x() - YigSynthGraphic::MOD_INPUT_X + (YigSynthGraphic::ELLIPSE_SIZE/2),
        //                       line().p2().y() - YigSynthGraphic::MOD_INPUT_Y + (YigSynthGraphic::ELLIPSE_SIZE/2));


        qreal distanceA = sqrt( pow(outputTriangle->pos().x() - line().p1().x(), 2)
                               + pow(outputTriangle->pos().y() - line().p1().y(), 2));

        qreal distanceB = sqrt( pow(line().p2().x() - outputTriangle->pos().x(), 2)
                               + pow(line().p2().y() - outputTriangle->pos().y(), 2));

        qreal distanceC = sqrt( pow(line().p1().x() - line().p2().x(), 2)
                                + pow(line().p1().y() - line().p2().y(), 2));
                //line().length();

        qreal angleTurn = acosf((pow(distanceB, 2) - pow(distanceA, 2) - pow(distanceC, 2)) / (2 * distanceA * distanceC))
                * 180 / PI;

        //qreal angleTurn = acosf((pow(distanceA, 2) + pow(distanceC, 2) - pow(distanceB, 2)) / (2 * distanceA * distanceC))
        //        * 180 / PI;

        //float angleTurn = getAngleABC(outputTriangle->pos(), line().p1(), line().p2());

        QPointF center = QPointF(outputTriangle->pos().x(), outputTriangle->pos().y() - (YigSynthGraphic::ELLIPSE_SIZE/2));
        qreal angle2 = atan2(line().p2().x() - line().p1().x(), line().p2().y() - line().p1().y()) * 180 / PI;
        qreal centerAngle = atan2(center.x() - line().p1().x(), center.y() - line().p1().y()) * 180 / PI;
        qreal modAngle = atan2(YigSynthGraphic::MOD_INPUT_X, YigSynthGraphic::MOD_INPUT_Y) * 180 / PI;
        outputTriangle->setRotation(180 - angle2);
        //qreal angleOffset = atan2(outputTriangle->pos().x() - line().p2().x(), outputTriangle->pos().y() - line().p2().y()) * 180 / PI;
        //float angleOffset = getAngleABC(outputTriangle->pos(), line().p2(), line().p1()) * 180 / PI;
        //qDebug() << outputTriangle->pos();
        qreal angleOffset = 180 - centerAngle - (180 - modAngle);
        outputTriangle->setRotation(180 - angle2 + angleOffset);

        //outputTriangle->moveBy(0, YigSynthGraphic::MOD_ELLIPSE_SIZE/2);
    }

    else {
        //outputTriangle->setRotation(angle+90);
        //outputTriangle->moveBy(-outputTriangle->pos().x(), -outputTriangle->pos().y());
        //outputTriangle->moveBy(line().p2().x(), line().p2().y());
        //outputTriangle->moveBy(0, YigSynthGraphic::AUDIO_ELLIPSE_SIZE/2);
    }*/

    //outputTriangle->setRotation(angle+90);
    //outputTriangle->setPos(line().p2().x(), line().p2().y());

}

float YigCableGraphic::getAngleABC(QPointF a, QPointF b, QPointF c)
{
    //qDebug() << a << b << c;
    /*
    QPointF ab = QPointF(b.x() - a.x(), b.y() - a.y());
    QPointF cb = QPointF(b.x() - c.x(), b.y() - c.y());

    // dot product
    float dot = (ab.x() * cb.x()) + (ab.y() * cb.y());

    // length square of both vectors
    float abSqr = (ab.x() * ab.x()) + (ab.y() * ab.y());
    float cbSqr = (cb.x() * cb.x()) + (cb.y() * cb.y());

    // square of cosine of the needed angle
    float cosSqr = ((dot * dot) / abSqr) / cbSqr;

    // This is a known trigonometric equalith:
    // cos(alpha * 2) = [cos(alpha)]^2 * 2 - 1
    float cos2 = (2 * cosSqr) - 1;

    float alpha2;

    if(cos2 <= -1)
    {
        alpha2 = PI;
    }

    else if( cos2 >= 1)
    {
        alpha2 = 0;
    }

    else
    {
        alpha2 = acosf(cos2);
    }

    float rslt = alpha2 / 2;
    float rs = rslt * 180 / PI;

    // Now revolve the ambiguities.
    // 1. If dot product of two vectors is negative - the angle is definitely
    // above 90 degrees. Still we have no information regarding the sign of the angle.

    // NOTE: This ambiguity is the consequence of our method: calculating the cosine
    // of the double angle. This allows us to get rid of calling sqrt.

    if(dot < 0)
    {
        rs = 180 - rs;
    }

    // 2. Determine the sign. For this we'll use the Determinant of two vectors.
    float det = (ab.x() * cb.y()) - (ab.y() * cb.y());
    if(det < 0)
    {
        rs = -rs;
    }

    return rs;*/

    // Calculate angle (in radians between two vectors pointing outwards from one center
    float ac = sqrt(pow(c.x() - a.x(), 2) +
                    pow(c.y() - a.y(), 2));
    float bc = sqrt(pow(c.x() - b.x(), 2) +
                    pow(c.y() - b.y(), 2));
    float ab = sqrt(pow(b.x() - a.x(), 2) +
                    pow(b.y() - a.y(), 2));
    return acosf((bc*bc+ac*ac-ab*ab) / (2*bc*ac));
}
